/*******************************************************************************
 * Level Master 2000
 * Copyright (C) 2010 Christian Herzog
 * For more information see http://code.google.com/p/levelmaster2000/
 *
 * This file is licensed under the terms of the MIT license, see
 * http://www.opensource.org/licenses/mit-license.php for further information.
 ******************************************************************************/

#include "LevelMaster2000.h"

//Commands:
#include "commands/SelectComponentsCommand.h"
#include "commands/SelectEntitiesCommand.h"
#include "commands/SelectEntityCommand.h"
#include "commands/PropertyChangeCommand.h"

#include "IPBObject.h"
#include "Logger.h"

#include "Entity.h"
#include "Component.h"
#include "Config.h"

//Qt stuff:
#include <QAction>

#include <QDockWidget>
#include <QDomDocument>

#include <QMenu>
#include <QMessageBox>

#include <QFile>

#include <QTreeView>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QUndoStack>
#include <QUndoView>
#include <QWebFrame>
#include <QWebView>
#include <QXmlStreamWriter>

int LevelMaster2000::DISALLOW_NEW_COMMAND = 0;

LevelMaster2000::LevelMaster2000( QWidget *parent, Qt::WFlags flags )
    : QMainWindow( parent, flags ),
    communicator( 0 ),
    componentBrowser( 0 ),
    entityExplorer( 0 ),
    levelManager( 0 ),
    propertyBrowser( 0 ),
    scene( 0 )
{
    ui.setupUi( this );

    Logger::instance()->init( this );

    //TODO: create
    // *init(); >> instanciates all the members, such as entityExplorer, communicator, etc.
    // *then call setupConnections()
    //      *connectEntityExplorer()
    //      *connectCommunicator() ...etc which does the actual connection.

    initCommonStuff();

    initCommunicator();

    initUndoView();

    initEntityExplorer();

    initPropertyBrowser();

    initLevelManager();

    //Put in onComponentInfo
    //initSettings();

    createActions();

    createMenus();

    addDockWidget( Qt::BottomDockWidgetArea, Logger::instance()->logWidget );
}

LevelMaster2000::~LevelMaster2000()
{
    delete communicator;
    delete componentBrowser;
    delete entityExplorer;
    delete propertyBrowser;
}

void LevelMaster2000::initCommunicator()
{
    communicator = new Communicator( this, scene->page()->mainFrame() );

    connect( communicator,
             SIGNAL( parsedLoadedSceneHierarchy( QList<IPBObject*> ) ),
             this,
             SLOT( onLoadedSceneHierarchy( QList<IPBObject*> ) ) );

    connect( communicator,
             SIGNAL( parsedComponentInfo( QList<ClassInfo> ) ),
             this,
             SLOT( onComponentInfo( QList<ClassInfo> ) ) );

}

void LevelMaster2000::onComponentInfo( QList<ClassInfo> componentInfo )
{
    //Now update componentLineEdit (the auto complete input):
    entityExplorer->initComponentCompleter(componentInfo);

    initComponentBrowser(componentInfo);

    initSettings();

    addDockWidget(Qt::RightDockWidgetArea, entityExplorer, Qt::Horizontal);
    addDockWidget(Qt::RightDockWidgetArea, propertyBrowser, Qt::Horizontal);

    //Dock entity explorer and property browser on each other,
    //so that they are tabbed:
    tabifyDockWidget( entityExplorer, propertyBrowser );

    //Set Entity Explorer to be the selected one:
    entityExplorer->raise();
}

//TODO: this should be put in entity explorer?
void LevelMaster2000::onLoadedSceneHierarchy( QList<IPBObject*> pbObjects )
{
    entityExplorer->tree->clear();

    foreach( IPBObject *object, pbObjects )
    {
        Group *group = dynamic_cast<Group*>( object );
        Entity *entity = dynamic_cast<Entity*>( object );

        if( group )
        {
            GroupTreeWidgetItem *groupParent = 0;
            if( group->parent )
            {
                groupParent = entityExplorer->findTreeItemByName<GroupTreeWidgetItem*>( group->parent->name );
            }
            GroupTreeWidgetItem *groupItem = new GroupTreeWidgetItem( groupParent, group );
            groupItem->setText( 0, group->name );
            QIcon groupIcon ("../../Cpp/ui/icons/group.png");
            groupItem->setIcon( 0, groupIcon );

            if( groupParent == 0 )
            {
                entityExplorer->tree->addTopLevelItem( groupItem );
            }
        }

        if( entity )
        {
            GroupTreeWidgetItem *groupItem = 0;
            if( entity->group )
            {
                groupItem = entityExplorer->findTreeItemByName<GroupTreeWidgetItem*>( entity->group->name );
            }

            //Add to entity explorer:
            EntityTreeWidgetItem *entityItem = new EntityTreeWidgetItem( groupItem, entity );
            entityItem->setText( 0, entity->name );
            QIcon entityIcon ("../../Cpp/ui/icons/entity.png");
            entityItem->setIcon( 0, entityIcon );
        }
    }

    entityExplorer->tree->sortItems( 0, Qt::AscendingOrder );
}

void LevelMaster2000::initCommonStuff()
{
    //Init undo stack:
    undoStack = new QUndoStack( this );

    //Activating some web settings:
    QWebSettings::globalSettings()->setAttribute( QWebSettings::PluginsEnabled, true );
    QWebSettings::globalSettings()->setAttribute( QWebSettings::AutoLoadImages, true );

    QWidget* sceneParent = this->findChild<QWidget*>( "centralWidget" ); //TODO: put strings in config file...
    if( sceneParent == 0 )
    {
        QMessageBox::critical( this,
                               "LevelMaster2000::initCommonStuff",
                               "Widget \"centralWidget\" not found." );
        qApp->exit( 1 ); //TODO: exit doesn't seem to work :/
        return;
    }

    QLayout* layout = this->findChild<QLayout*>( "mainLayout" );
    if( layout == 0 )
    {
        QMessageBox::critical( this,
                               "LevelMaster2000::initCommonStuff",
                               "Widget \"mainLayout\" not found." );
        qApp->exit( 1 );
        return;
    }

    //Create a new web view and add:
    scene = new QWebView( sceneParent );
    scene->load( QUrl( "../../AS3/bin/LevelMaster2000.html" ) );
    //scene->load( QUrl( "http://www.scrambled-egg.net/levelmaster2000/editor/bin/LevelMaster2000.html" ) );

    scene->page()->mainFrame()->addToJavaScriptWindowObject( "commandXml", this );
    //scene->page()->mainFrame()->addToJavaScriptWindowObject( "logger", Logger::log );

    layout->addWidget( scene );

    //ACTIONS:
    QAction* liveMode = this->findChild<QAction*>("actionLiveMode");
    liveMode->setChecked( true );
    connect( liveMode,
             SIGNAL( toggled( bool ) ),
             this,
             SLOT( toggleLiveMode( bool ) ) );
}

void LevelMaster2000::initComponentBrowser( QList<ClassInfo> &componentInfo )
{
    componentBrowser = new ComponentBrowser( this, componentInfo );

    connect( componentBrowser,
             SIGNAL( createComponent( QString, QString ) ),
             entityExplorer,
             SLOT( createComponent( QString, QString ) ) );
}

void LevelMaster2000::initUndoView()
{
    undoView = new QUndoView( undoStack );
    undoView->setWindowTitle( tr( "Command List" ) );
    undoView->show();
    undoView->setAttribute( Qt::WA_QuitOnClose, false );
}

void LevelMaster2000::initSettings()
{
    //Load settings.xml:
    QDomDocument settingsDoc( "settingsDoc" );
    //QFile file( "../data/settings.xml" );
    QFile file( Config::SETTINGS_XML );

    if( !file.open( QIODevice::ReadOnly ) )
    {
        Logger::instance()->addLogMessage( Logger::LL_ERROR, "LevelMaster2000::initSettings", "Failed to load settings.xml!" );
        return;
    }

    if( !settingsDoc.setContent( &file ) )
    {
        file.close();
        Logger::instance()->addLogMessage( Logger::LL_ERROR, "LevelMaster2000::initSettings", "Could not set content of settings.xml!" );
        return;
    }

    file.close();

    QDomElement root = settingsDoc.documentElement();
    if( root.tagName() != "settings" )
    {
        Logger::instance()->addLogMessage( Logger::LL_ERROR, "LevelMaster2000::initSettings", "Root tag not of type settings!" );
        return;
    }

    //Everything is valid, now we can parse the settings file:
    QDomElement settingsNode = root.firstChildElement();
    while( !settingsNode.isNull() )
    {
        if( settingsNode.tagName() == "workspace" )
        {
            levelManager->setWorkspaceDir( settingsNode.attribute( "path" ) );
        }

        settingsNode = settingsNode.nextSiblingElement();
    }
}

void LevelMaster2000::initLevelManager()
{
    levelManager = new LevelManager( this );

    connect( levelManager,
             SIGNAL( loadLevel( int, QString ) ),
             communicator,
             SLOT( sendLoadLevel( int, QString ) ) );

    connect( levelManager,
             SIGNAL( requestSaveCurrentLevel( int, QString ) ),
             communicator,
             SLOT( sendRequestSaveLevel( int, QString ) ) );

    connect( levelManager,
             SIGNAL( workspaceChanged( QString ) ),
             communicator,
             SLOT( workspaceChanged( QString ) ) );

    connect( communicator,
             SIGNAL( parsedLevelState( QString ) ),
             levelManager,
             SLOT( onSaveLevel( QString ) ) );
}

void LevelMaster2000::createActions()
{
    saveCurrentLevelAction = new QAction( tr( "&Save Current Level" ), this );
    connect( saveCurrentLevelAction,
             SIGNAL( triggered() ),
             levelManager,
             SLOT( saveCurrentLevel() ) );


    showLevelManagerAction = new QAction( tr( "&Level Manager ..." ), this );
    connect( showLevelManagerAction,
             SIGNAL( triggered() ),
             levelManager,
             SLOT( show() ) );

    setWorkspaceAction = new QAction( tr( "&Workspace ..." ), this );
    connect( setWorkspaceAction,
             SIGNAL( triggered() ),
             levelManager,
             SLOT( setWorkspace() ) );


    undoAction = undoStack->createUndoAction( this, tr( "&Undo" ) );
    undoAction->setShortcuts( QKeySequence::Undo );

    redoAction = undoStack->createRedoAction( this, tr( "&Redo" ) );
    redoAction->setShortcuts( QKeySequence::Redo );

    exitAction = new QAction( tr( "&Exit" ), this );
    exitAction->setShortcuts( QKeySequence::Quit );
    connect( exitAction, SIGNAL( triggered() ), this, SLOT( close() ) );
}

void LevelMaster2000::createMenus()
{
    //Scene:
    fileMenu = menuBar()->addMenu( tr( "&File" ) );

    fileMenu->addAction( saveCurrentLevelAction );
    fileMenu->addAction( showLevelManagerAction );
    fileMenu->addSeparator();
    fileMenu->addAction( setWorkspaceAction );

    //Other:
    fileMenu->addSeparator();
    fileMenu->addAction( exitAction );

    //Edit:
    editMenu = menuBar()->addMenu( tr( "&Edit" ) );
    editMenu->addAction( undoAction );
    editMenu->addAction( redoAction );
    editMenu->addSeparator();

    //Window:
    windowMenu = menuBar()->addMenu( tr( "&Window" ) );

    //Help:
    helpMenu = menuBar()->addMenu( tr( "&Help" ) );
}

void LevelMaster2000::initPropertyBrowser()
{
    propertyBrowser = new PropertyBrowser(this, communicator, undoStack);

    connect( communicator,
             SIGNAL( parsedComponentData( QString, QMap<QString, QVariant>& ) ),
             propertyBrowser,
             SLOT( onComponentData( QString, QMap<QString, QVariant>& ) ) );
}

void LevelMaster2000::initEntityExplorer()
{
    entityExplorer = new EntityExplorer( this, undoStack );

    //Entity explorer requests a new entity:
    //TODO: rename to sendRequestCreateEntity....
    connect( entityExplorer,
             SIGNAL( requestCreateEntity( QString, QString ) ),
             communicator,
             SLOT( sendCreateEntity( QString, QString ) ) );

    connect( entityExplorer,
             SIGNAL( requestCreateGroup( QString, QString ) ),
             communicator,
             SLOT( sendCreateGroup( QString, QString ) ) );

    connect( entityExplorer,
             SIGNAL( selectEntities( QList<Entity*> ) ),
             communicator,
             SLOT( sendSelectEntities( QList<Entity*> ) ) );

    connect( communicator,
             SIGNAL( parsedRequestSelectEntity( QString ) ),
             entityExplorer,
             SLOT( onRequestSelectEntity( QString ) ) );

    connect( communicator,
             SIGNAL( parsedNewEntity( QString, QString ) ),
             entityExplorer,
             SLOT( onNewEntity( QString, QString ) ) );

    connect( communicator,
             SIGNAL( parsedNewGroup( QString, QString ) ),
             entityExplorer,
             SLOT( onNewGroup( QString, QString ) ) );

    connect( communicator,
             SIGNAL( parsedDeleteObjects( QStringList ) ),
             entityExplorer,
             SLOT( onDeleteObjects( QStringList ) ) );

    connect( entityExplorer,
             SIGNAL( deleteObjects( QStringList ) ),
             communicator,
             SLOT( sendDeleteObjects( QStringList ) ) );

    connect( entityExplorer,
             SIGNAL( sigDeleteComponent( QString, QString ) ),
             communicator,
             SLOT( sendDeleteComponent( QString, QString ) ) );

    connect( entityExplorer,
             SIGNAL( showComponentBrowser( bool ) ),
             this,
             SLOT( showComponentBrowser( bool ) ) );

    connect( entityExplorer,
             SIGNAL( requestCreateComponent( QString, QString, QString ) ),
             communicator,
             SLOT( sendCreateComponent( QString, QString, QString ) ) );

    connect( communicator,
             SIGNAL( parsedNewComponent( QString, QString, QString ) ),
             entityExplorer,
             SLOT( onNewComponent( QString, QString, QString ) ) );

    connect( communicator,
             SIGNAL( parsedDeleteComponent( QString, QString ) ),
             entityExplorer,
             SLOT( onDeleteComponent( QString, QString ) ) );


    //Get and set entity XML:
    connect( entityExplorer,
             SIGNAL( requestGetEntityXml( QString ) ),
             communicator,
             SLOT( sendGetEntityXml( QString ) ) );

    connect( entityExplorer,
             SIGNAL( requestSetEntityXml( QString, QString ) ),
             communicator,
             SLOT( sendSetEntityXml( QString, QString ) ) );

    connect( communicator,
             SIGNAL( parsedGetEntityXml( QString, QString ) ),
             entityExplorer,
             SLOT( onGetEntityXml( QString, QString ) ) );

    connect( communicator,
             SIGNAL( parsedSetEntityXml( QString, QString ) ),
             entityExplorer,
             SLOT( onSetEntityXml( QString, QString ) ) );


    //Get and set component XML:
    connect( entityExplorer,
             SIGNAL( requestGetComponentXml( QString, QString ) ),
             communicator,
             SLOT( sendGetComponentXml( QString, QString ) ) );

    connect( entityExplorer,
             SIGNAL( requestSetComponentXml( QString, QString, QString ) ),
             communicator,
             SLOT( sendSetComponentXml( QString, QString, QString ) ) );

    connect( communicator,
             SIGNAL( parsedGetComponentXml( QString, QString, QString, QString ) ),
             entityExplorer,
             SLOT( onGetComponentXml( QString, QString, QString, QString ) ) );
}

void LevelMaster2000::showComponentBrowser( bool show )
{
    if( show )
    {
        componentBrowser->setFloating( true );
        componentBrowser->show();
    }

    else
    {
        componentBrowser->hide();
    }
}

void LevelMaster2000::toggleLiveMode( bool isChecked )
{
    float newTimeScale = 0.0f;

    if( isChecked )
    {
        newTimeScale = 1.0f;
    }

    QString xmlString;
    QXmlStreamWriter stream( &xmlString );
    stream.setAutoFormatting( false );
    stream.writeStartElement( "command" );
    stream.writeAttribute( "type", "timeScale" ); //TODO: const me!
    stream.writeAttribute( "timeScale", QString::number( newTimeScale ) );
    stream.writeEndElement();

    communicator->sendXmlString( xmlString );
}

QString LevelMaster2000::getCommandXmlData()
{
    return xmlDataString;
}

void LevelMaster2000::setCommandXmlData( QString xml )
{
    xmlDataString = xml;

    domDocument.setContent( xml, true );

    if( propertyBrowser )
    {
        propertyBrowser->clearProperties();
    }
    communicator->parseCommands( domDocument.documentElement() );
}

//TODO: put in logger class, if possible
void LevelMaster2000::addLogMessage( QString level, QString loggerName, QString message )
{
    if( level == "ERROR" )
    {
        Logger::instance()->addLogMessage( Logger::LL_ERROR, loggerName, message );
    }
    else if( level == "WARNING" )
    {
        Logger::instance()->addLogMessage( Logger::LL_WARNING, loggerName, message );
    }
    else
    {
        Logger::instance()->addLogMessage( Logger::LL_INFO, loggerName, message );
    }
}
